package io.gitee.chearnee.fw.common.processor;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author cn
 * @version 1.0
 * @description
 * @date 2022/7/11 19:01
 */
public class Processor<S, R> implements IProcessor<S, R> {
  @Override
  public void destroy() {
    if (processors.isEmpty()) {
      return;
    }
    for (IProcessor<S, R> processor : processors) {
      processor.destroy();
    }
  }

  private List<? extends IProcessor<S, R>> processors = new ArrayList<>();


  public void setFilters(List<? extends IProcessor<S, R>> processors) {
    List<? extends IProcessor<S, R>> collect = processors.stream().filter(IProcessor::support).collect(Collectors.toList());
    AnnotationAwareOrderComparator.sort(collect);
    this.processors = new ArrayList<>(collect);
  }

  @Override
  public boolean beforeProcess(S s) {
    return true;
    // TODO document why this method is empty
  }

  @Override
  public R processing(S s, IProcessorChain<S, R> iProcessorChain) {
    return new VirtualProcessChain<S, R>(iProcessorChain, processors).nextProcessor(s);
  }

  @Override
  public void afterProcess(S s, R r) {
    // TODO document why this method is empty
  }

  private static class VirtualProcessChain<S, R> implements IProcessorChain<S, R> {

    private final IProcessorChain<S, R> originalChain;

    private final List<? extends IProcessor<S, R>> additionalFilters;

    private int currentPosition = 0;

    VirtualProcessChain(IProcessorChain<S, R> chain,
        List<? extends IProcessor<S, R>> additionalFilters) {
      this.originalChain = chain;
      this.additionalFilters = additionalFilters;
    }

    @Override
    public R nextProcessor(S s) {
      if (this.currentPosition == this.additionalFilters.size()) {
        return this.originalChain.nextProcessor(s);
      } else {
        this.currentPosition++;
        IProcessor<S, R> nextFilter = this.additionalFilters.get(
                this.currentPosition - 1);
        R processing = null;
        if (!nextFilter.beforeProcess(s)) {
          return processing;
        }
        processing = nextFilter.processing(s, this);
        nextFilter.afterProcess(s, processing);
        return processing;
      }
    }
  }
}
